package be.vanderveer.services.webthumb;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

/**
 * The WebThumb class is a java connector to the WebThumb api found at
 * http://bluga.net/webthumb/. This service allows you to get a thumbnail of a
 * submitted url.
 * 
 * This class perfoms the following tasks with the API
 * <ul>
 * <li>Submitting an URL</li>
 * <li>Delivering reports on progress</li>
 * <li>Fetches the image and returns it as a BufferedImage</li>
 * </ul>
 * 
 * @author Roderik van der Veer
 */
public class WebThumb {

	/**
	 * The WebThumb API key. Request your own by registering at
	 * http://bluga.net/webthumb/. You will get 250 free thumbnails per month
	 * but you can buy extra!
	 */
	private String apiKey;
	/**
	 * The URI of the WebThumb API. Added as a variable to allow for the moving
	 * of the API without the need to recompile this code.
	 */
	private URI webThumbURI;
	/**
	 * If this class has to run through a proxy, this variable will hold the
	 * host.
	 */
	private URI proxyHost;
	/**
	 * The port of the proxy host.
	 */
	private Integer proxyPort;
	/**
	 * The logger instance for this class.
	 */
	private final Log log = LogFactory.getLog(WebThumb.class);

	/**
	 * Constructs a API connector object for the WebThumb service.
	 * 
	 * @param apiKey
	 *            the personal key you get after registering for the service.
	 */
	public WebThumb(final String apiKey) {
		this(apiKey, null, null, null);
		final String url = "http://webthumb.bluga.net/api.php";
		try {
			setWebThumbURI(new URI(url));
		} catch (final URISyntaxException e) {
			log.fatal("The URI: " + url + " is invalid.", e);
		}
	}

	/**
	 * Constructs a API connector object for the WebThumb service.
	 * 
	 * @param apiKey
	 *            the personal key you get after registering for the service.
	 * @param webThumbURL
	 *            the uri of the webthumb api.
	 * @throws URISyntaxException
	 *             thrown if the webthumb api url is invalid
	 */
	public WebThumb(final String apiKey, final String webThumbURL) throws URISyntaxException {
		this(apiKey, null, null, null);
		try {
			setWebThumbURI(new URI(webThumbURL));
		} catch (final URISyntaxException e) {
			log.fatal("The URI: " + webThumbURL + " is invalid.", e);
			throw new RuntimeException("The URI: " + webThumbURL + " is invalid.", e);
		}
	}

	/**
	 * Constructs a API connector object for the WebThumb service.
	 * 
	 * @param apiKey
	 *            the personal key you get after registering for the service.
	 * @param webThumbURI
	 *            the uri of the webthumb api.
	 * @throws URISyntaxException
	 *             thrown if the webthumb api url is invalid
	 */
	public WebThumb(final String apiKey, final URI webThumbURI) {
		this(apiKey, webThumbURI, null, null);
	}

	/**
	 * Constructs a API connector object for the WebThumb service.
	 * 
	 * @param apiKey
	 *            the personal key you get after registering for the service.
	 * @param webThumbURI
	 *            the uri of the webthumb api.
	 * @param proxyHost
	 *            the host of the proxy server to use.
	 * @param proxyPort
	 *            the port of the proxy server to use.
	 */
	public WebThumb(final String apiKey, final URI webThumbURI, final URI proxyHost, final Integer proxyPort) {
		super();
		setApiKey(apiKey);
		setWebThumbURI(webThumbURI);
		setProxyHost(proxyHost);
		setProxyPort(proxyPort);
	}

	/**
	 * This method will send a request to the WebThumb api to make a screenshot
	 * of the provided URI.
	 * 
	 * @param url
	 *            the url that has to be converted to a screenshot.
	 * @return the response from the api, wrapped up in a WebThumbJobResponse
	 *         object
	 */
	public WebThumbJobResponse requestScreenshot(final String url) throws URISyntaxException {
		return requestScreenshot(new URI(url), 1024, 768);
	}

	/**
	 * This method will send a request to the WebThumb api to make a screenshot
	 * of the provided URI.
	 * 
	 * @param url
	 *            the url that has to be converted to a screenshot.
	 * @return the response from the api, wrapped up in a WebThumbJobResponse
	 *         object
	 */
	public WebThumbJobResponse requestScreenshot(final URI url, final int width, final int height) {
		final PostMethod post = new PostMethod(getWebThumbURI().toASCIIString());
		final Element rootelem = new Element("webthumb");
		final Element apikeyelem = new Element("apikey");
		apikeyelem.setText(getApiKey());
		rootelem.addContent(apikeyelem);
		final Element requestelem = new Element("request");
		final Element urlelem = new Element("url");
		urlelem.setText(url.toASCIIString());
		requestelem.addContent(urlelem);
		final Element widthelem = new Element("width");
		widthelem.setText(width + "");
		requestelem.addContent(widthelem);
		final Element heightelem = new Element("height");
		heightelem.setText(height + "");
		requestelem.addContent(heightelem);
		rootelem.addContent(requestelem);
		final Document doc = new Document(rootelem);
		final XMLOutputter serializer = new XMLOutputter();
		String xml = serializer.outputString(doc);
		final RequestEntity entity;
		try {
			entity = new StringRequestEntity(xml, "text/xml", "UTF-8");
		} catch (final UnsupportedEncodingException e) {
			log.fatal("The xml is unreadable", e);
			throw new RuntimeException("The xml is unreadable", e);
		}
		post.setRequestEntity(entity);
		final HttpClient httpclient = new HttpClient();
		if (getProxyHost() != null && !StringUtils.isBlank(getProxyHost().toASCIIString())) {
			httpclient.getHostConfiguration().setProxy(getProxyHost().toASCIIString(), getProxyPort());
			System.setProperty("http.proxyHost", getProxyHost().toASCIIString());
			System.setProperty("http.proxyPort", getProxyPort().toString());
		}
		try {
			final int result = httpclient.executeMethod(post);
			if (result == 200) {
				xml = post.getResponseBodyAsString();
				final WebThumbJobResponse wtr = new WebThumbJobResponse(xml);
				return wtr;
			} else {
				throw new IOException("The server is not online! Errorcode: " + result);
			}
		} catch (final Exception e) {
			throw new RuntimeException("Something went wrong, check the stacktrace", e);
		} finally {
			post.releaseConnection();
		}
	}

	/**
	 * This method will check with the api to see if the images are ready.
	 * Please call this method only after waiting the time noted in the
	 * {@link WebThumbJobResponse}.
	 * 
	 * @param webthumbid
	 *            the id of the images requested
	 * @return the response from the api, wrapped up in a WebThumbStatusResponse
	 *         object
	 */
	public WebThumbStatusResponse checkImages(final String webthumbid) {
		final PostMethod post = new PostMethod(getWebThumbURI().toASCIIString());
		final Element rootelem = new Element("webthumb");
		final Element apikeyelem = new Element("apikey");
		apikeyelem.setText(getApiKey());
		rootelem.addContent(apikeyelem);
		final Element statuselem = new Element("status");
		final Element jobelem = new Element("job");
		jobelem.setText(webthumbid);
		statuselem.addContent(jobelem);
		rootelem.addContent(statuselem);
		final Document doc = new Document(rootelem);
		final XMLOutputter serializer = new XMLOutputter();
		String xml = serializer.outputString(doc);
		final RequestEntity entity;
		try {
			entity = new StringRequestEntity(xml, "text/xml", "UTF-8");
		} catch (final UnsupportedEncodingException e) {
			log.fatal("The xml is unreadable", e);
			throw new RuntimeException("The xml is unreadable", e);
		}
		post.setRequestEntity(entity);
		final HttpClient httpclient = new HttpClient();
		if (getProxyHost() != null && !StringUtils.isBlank(getProxyHost().toASCIIString())) {
			httpclient.getHostConfiguration().setProxy(getProxyHost().toASCIIString(), getProxyPort());
			System.setProperty("http.proxyHost", getProxyHost().toASCIIString());
			System.setProperty("http.proxyPort", getProxyPort().toString());
		}
		try {
			final int result = httpclient.executeMethod(post);
			if (result == 200) {
				xml = post.getResponseBodyAsString();
				final WebThumbStatusResponse wsr = new WebThumbStatusResponse(xml);
				return wsr;
			} else {
				throw new IOException("The server is not online! Errorcode: " + result);
			}
		} catch (final Exception e) {
			throw new RuntimeException("Something went wrong, check the stacktrace", e);
		} finally {
			post.releaseConnection();
		}
	}

	private void setApiKey(final String apiKey) {
		this.apiKey = apiKey;
	}

	private void setProxyHost(final URI proxyHost) {
		this.proxyHost = proxyHost;
	}

	private void setProxyPort(final Integer proxyPort) {
		this.proxyPort = proxyPort;
	}

	private void setWebThumbURI(final URI webThumbURI) {
		this.webThumbURI = webThumbURI;
	}

	private String getApiKey() {
		return apiKey;
	}

	private URI getProxyHost() {
		return proxyHost;
	}

	private Integer getProxyPort() {
		return proxyPort;
	}

	private URI getWebThumbURI() {
		return webThumbURI;
	}
}
